package cn.com.bluemoon.daps.sync.core.sync.compare;

import cn.com.bluemoon.daps.common.exception.DapThrowException;
import cn.com.bluemoon.daps.common.sql.IDbDataSource;
import cn.com.bluemoon.daps.model.entity.DapDataModelSubordinateKey;
import cn.com.bluemoon.daps.model.entity.DapDataModelTable;
import cn.com.bluemoon.daps.model.entity.DapDataModelTableField;
import cn.com.bluemoon.daps.model.entity.DapDataModelTablePartition;
import cn.com.bluemoon.daps.sync.base.CompareResult;
import cn.com.bluemoon.daps.sync.base.ModifyType;
import cn.com.bluemoon.daps.sync.core.sync.EntityCompareUtils;
import cn.com.bluemoon.daps.sync.core.sync.GlobalConfig;
import cn.com.bluemoon.daps.sync.core.sync.Tuple2;
import cn.com.bluemoon.daps.sync.core.sync.sql.BmSql;
import com.alibaba.druid.DbType;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 抽象对比逻辑
 * <p>1.v1存在 v2不存在 => drop
 * <p>2.v1不存在 v2存在 => create
 * <p>3.v1存在 v2存在   => alter
 * 进行抽象，同时提供对应的抽象方法
 * 每次对比利用，cn/com/bluemoon/demo/model/core/compare/BaseComparator.java:47的collector
 * 进行结果收集
 *
 * @author Jarod.Kong
 * @date 2021/1/14 9:38
 */
@Slf4j
public abstract class BaseComparator<T extends IDbDataSource, R> implements IComparator<T, R> {
    protected final DapDataModelTable newTable;
    protected final DapDataModelTable oldTable;
    protected DbType dbType;
    protected BmSql bmSql;
    protected GlobalConfig globalConfig;
    /**
     * 数据传输
     */
    private List<Tuple2<DapDataModelTableField>> v1AndV2DbFields;
    private List<Tuple2<DapDataModelSubordinateKey>> v1AndV2DbIndexes;
    private List<Tuple2<DapDataModelTablePartition>> v1AndV2DbPartitions;
    private ModifyType.ModifyLevel modifyLevel;
    private T oldT;
    private T newT;

    public BaseComparator(GlobalConfig globalConfig, Tuple2<DapDataModelTable> tableTuple2) {
        this.globalConfig = globalConfig;
        this.dbType = globalConfig.getDbType();
        this.bmSql = globalConfig.isCheckSqlLayer() ? BmSql.use(dbType).openLayerCheck() : BmSql.use(dbType);
        if (globalConfig.isNoBlackQuote()) {
            bmSql.noBlackQuote();
            log.info("启动SQL生成不加反斜杠开关");
        }
        this.oldTable = tableTuple2.getOldT();
        this.newTable = tableTuple2.getNewT();
    }

    @Override
    public List<CompareResult<R>> compare(T oldT, T newT) {
        this.oldT = oldT;
        this.newT = newT;
        List<R> collector = new ArrayList<>();
        ModifyType modifyType = ModifyType.NONE;
        if (oldT == null && newT != null) {
            modifyType = ModifyType.CREATE;
            create0(collector);
        } else if (oldT != null && newT == null) {
            modifyType = ModifyType.DROP;
            drop0(collector);
        } else if (oldT != null && newT != null) {
            if (!oldT.equals(newT)) {
                modifyType = ModifyType.ALTER;
                change0(collector);
            }
        }
        // 统一增加采集内容
        final ModifyType finalModifyType = modifyType;
        return collector.stream().map(r -> {
            final String[] sqlCompareFields;
            if (getOldT() != null) sqlCompareFields = getOldT().sqlCompareFields();
            else if (getNewT() != null) sqlCompareFields = getNewT().sqlCompareFields();
            else sqlCompareFields = new String[0];
            Map<String, List<Object>> res = EntityCompareUtils.compareFields(getOldT(), getNewT(), sqlCompareFields);
            return CompareResult.<R>builder()
                    .desc(res)
                    .tableId(getTableId())
                    .modelId(globalConfig.getModel().getId())
                    .modelName(globalConfig.getModel().getName())
                    .dbName(globalConfig.getDsDbName().orElse(null))
                    .tableName(getTableName())
                    .modifyType(finalModifyType)
                    .modifyLevel(modifyLevel)
                    .highDangerStats(ModifyType.DROP.equals(finalModifyType))// 有drop就高危
                    .data(r)
                    .build();
        }).sorted((o1, o2) -> { // 先级别asc
            if (o1 == null) return -1;
            if (o2 == null) return 1;
            return o1.getModifyLevel().getRank().compareTo(o2.getModifyLevel().getRank());
        }).sorted((o1, o2) -> { // 再动作asc
            if (o1 == null) return -1;
            if (o2 == null) return 1;
            // 索引排序，先删除后创建（重建）
            if (ModifyType.ModifyLevel.INDEX.equals(o1.getModifyLevel()) || ModifyType.ModifyLevel.INDEX.equals(o2.getModifyLevel())){
                return o2.getModifyType().getRank().compareTo(o1.getModifyType().getRank());
            }
            return o1.getModifyType().getRank().compareTo(o2.getModifyType().getRank());
        }).collect(Collectors.toList());
    }

    public abstract void change0(List<R> collector);

    public abstract void drop0(List<R> collector);

    public abstract void create0(List<R> collector);

    protected String getTableId() {
        if (oldTable != null && oldTable.getId() != null) {
            return oldTable.getId();
        }
        if (newTable != null && newTable.getId() != null) {
            return newTable.getId();
        }
        log.debug("发现数据库中存在表{}，而模型中不存在，此时表id为空，忽略; \n 提示，由于{}={}, {}进行删除",
                getTableName(), "cn.com.bluemoon.daps.model.core.sync.GlobalConfig#canDropDbTable()",
                globalConfig.isCanDropDbTable(), globalConfig.isCanDropDbTable() ? "" : "不");
        // 要是允许删除库表则对该表名数据增加表id
        if (globalConfig.isCanDropDbTable()) {
            String dropDbTableId = IdWorker.getIdStr();
            if (oldTable != null && oldTable.getId() == null) {
                oldTable.setId(dropDbTableId);
                return dropDbTableId;
            }
            if (newTable != null && newTable.getId() == null) {
                newTable.setId(dropDbTableId);
                return dropDbTableId;
            }
        }
        return null;
    }

    protected String getTableName() {
        // 先拿新的表名，可能存在表名修改的情况
        if (newTable != null && newTable.getEngName() != null) {
            return newTable.getEngName();
        }
        if (oldTable != null && oldTable.getEngName() != null) {
            return oldTable.getEngName();
        }
        throw new DapThrowException("对比数据，不存在表name，排查！");
    }

    public List<Tuple2<DapDataModelTableField>> getV1AndV2DbFields() {
        if (v1AndV2DbFields == null) {
            return Lists.newArrayList();
        }
        return v1AndV2DbFields;
    }

    public void setV1AndV2DbFields(List<Tuple2<DapDataModelTableField>> v1AndV2DbFields) {
        this.v1AndV2DbFields = v1AndV2DbFields;
    }

    /**
     * @return 获取新的有序字段列表
     */
    public List<DapDataModelTableField> getNewTFieldsOrderByFieldRank() {
        return getV1AndV2DbFields().stream()
                .map(Tuple2::getNewT)
                .filter(Objects::nonNull)
                .sorted(Comparator.comparingInt(DapDataModelTableField::getFieldRank))
                .collect(Collectors.toList());
    }

    /**
     * @return 获取旧的有序字段列表
     */
    public List<DapDataModelTableField> getOldTFieldsOrderByFieldRank() {
        return getV1AndV2DbFields().stream()
                .map(Tuple2::getOldT)
                .filter(Objects::nonNull)
                .sorted(Comparator.comparingInt(DapDataModelTableField::getFieldRank))
                .collect(Collectors.toList());
    }

    public List<Tuple2<DapDataModelSubordinateKey>> getV1AndV2DbIndexes() {
        if (v1AndV2DbIndexes == null) {
            return Lists.newArrayList();
        }
        return v1AndV2DbIndexes;
    }

    public void setV1AndV2DbIndexes(List<Tuple2<DapDataModelSubordinateKey>> v1AndV2DbIndexes) {
        this.v1AndV2DbIndexes = v1AndV2DbIndexes;
    }

    public List<Tuple2<DapDataModelTablePartition>> getV1AndV2DbPartitions() {
        if (v1AndV2DbPartitions == null) {
            return Lists.newArrayList();
        }
        return v1AndV2DbPartitions;
    }

    public void setV1AndV2DbPartitions(List<Tuple2<DapDataModelTablePartition>> v1AndV2DbPartitions) {
        this.v1AndV2DbPartitions = v1AndV2DbPartitions;
    }

    public T getOldT() {
        return oldT;
    }

    public void setOldT(T oldT) {
        this.oldT = oldT;
    }

    public T getNewT() {
        return newT;
    }

    public void setNewT(T newT) {
        this.newT = newT;
    }

    public void setModifyLevel(ModifyType.ModifyLevel modifyLevel) {
        this.modifyLevel = modifyLevel;
    }
}
